#pragma once
#include"../net.hpp"
#include<future>
#include<mutex>
#include<utility>

// 这个模块负责提供响应回调和发送接口 : 针对所有消息类型
enum class MSG_REQ_TYPE
{
    SYNC,       //同步
    ASYNC,      //异步
    CALLBACK    //回调
};

class ReqDescription
{
public:
    using ptr = std::shared_ptr<ReqDescription>;
    using callBack = std::function<void(BaseMessage::ptr)>;
    ReqDescription(BaseMessage::ptr req, MSG_REQ_TYPE type, callBack cb) 
        :_msg(req), _type(type), _cb(cb)
    {}
    
    BaseMessage::ptr _msg;
    MSG_REQ_TYPE _type;
    // 结果保存
    std::promise<BaseMessage::ptr> _response;
    callBack _cb;
};

class ReqManager
{
public:
    using ptr = std::shared_ptr<ReqManager>;
    ReqManager() {}

    ReqDescription::ptr getReqDescription(const std::string& id)
    {
        std::lock_guard<std::mutex> lock(_mutex);
        auto it = _requests.find(id);
        if (it != _requests.end())
        {
            return it->second;
        }
        return ReqDescription::ptr(nullptr);
    }

    ReqDescription::ptr createReqDescription(BaseMessage::ptr msg, MSG_REQ_TYPE type, ReqDescription::callBack cb = nullptr)
    {
        std::lock_guard<std::mutex> lock(_mutex);
        ReqDescription::ptr reqD = std::make_shared<ReqDescription>(msg, type, cb);
        auto peer = _requests.insert(std::make_pair(msg->getId(), reqD));
        if (peer.second)
        {
            return reqD;
        }
        return ReqDescription::ptr(nullptr);
    }

    void removeReqDestriptio(const std::string& id)
    {
        std::lock_guard<std::mutex> lock(_mutex);
        _requests.erase(id);
    }

private:
    std::mutex _mutex;
    std::unordered_map<std::string, ReqDescription::ptr> _requests;
};

class Requestor
{
public:
    using ptr =  std::shared_ptr<Requestor>;
    Requestor() : _manager(new ReqManager) {}

    // 注册到dispatcher模块
    void onResponse(BaseConnection::ptr conn, BaseMessage::ptr msg, BaseProtocol::ptr protocol)
    {
        LOG_DEBUG << "收到消息：" << typeid(*(msg.get())).name();
        std::string id = msg->getId();
        ReqDescription::ptr reqD = _manager->getReqDescription(id);
        if (reqD == nullptr)
        {
            LOG_ERROR << "请求管理信息出错！";
            return;
        }

        if (reqD->_type == MSG_REQ_TYPE::CALLBACK)
        {
            if (reqD->_cb) reqD->_cb(msg);
            else LOG_WARN << "没有回调函数";
        }
        else if (reqD->_type == MSG_REQ_TYPE::ASYNC || reqD->_type == MSG_REQ_TYPE::SYNC)
        {
            reqD->_response.set_value(msg);
        }
        else
        {
            LOG_WARN << "未知类型调用";
        }
    }

    //异步结果
    std::pair<bool, std::future<BaseMessage::ptr>> sendAsync(BaseConnection::ptr conn,  BaseMessage::ptr req, BaseProtocol::ptr protocol)
    {
        ReqDescription::ptr reqD = _manager->createReqDescription(req, MSG_REQ_TYPE::ASYNC);
        bool ret = sendDatas(conn, req, protocol);
        if (ret) return std::make_pair(ret, std::move(reqD->_response.get_future())); // 移动
        else return std::make_pair(ret, std::future<BaseMessage::ptr>());
    }

    // 同步结果
    std::pair<bool, BaseMessage::ptr> sendSync(BaseConnection::ptr conn, BaseMessage::ptr req, BaseProtocol::ptr protocol)
    {
        ReqDescription::ptr reqD = _manager->createReqDescription(req, MSG_REQ_TYPE::SYNC);
        bool ret = sendDatas(conn, req, protocol);
        if (ret)
        {
            auto res = reqD->_response.get_future().get();
            return std::make_pair(ret, res);
        }
        return std::make_pair(ret, BaseMessage::ptr(nullptr));
    }

    //回调
    bool sendCallBack(BaseConnection::ptr conn, BaseMessage::ptr req, BaseProtocol::ptr protocol, ReqDescription::callBack cb)
    {
        _manager->createReqDescription(req, MSG_REQ_TYPE::CALLBACK, cb);
        return sendDatas(conn, req, protocol);
    }

    void remove(const std::string& id)
    {
        _manager->removeReqDestriptio(id);
        LOG_DEBUG << "消息处理完毕，移除管理";
    }

private:
    bool sendDatas(BaseConnection::ptr conn, BaseMessage::ptr req, BaseProtocol::ptr protocl)
    {
        std::string str_req = protocl->serialize(req);
        if (str_req.empty())
        {
            return false;
        }
        
        if (conn->connected())
        {
            conn->send(str_req);
            LOG_DEBUG << "发送成功：\n" << str_req;
            return true;
        }
        LOG_WARN << "发送失败，连接断开";
        return false;
    }

private:
    ReqManager::ptr _manager;
};